#ifndef _MEM_MANAGE_H
#define _MEM_MANAGE_H

#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/sem.h>

#define TOTAL_LEN 500000

//-----------Using for recombine-----------------
#define MEM_NAME "openmem.c"
#define MEM_ID 1000

#define SEM_NAME "opensem.c"
#define SEM_ID 100
//----------------------------------------------

//-----------Using for mailparse-----------------
#define M_MEM_NAME "creatmem.c"
#define M_MEM_ID 1001

#define M_SEM_NAME "creatsem.c"
#define M_SEM_ID 101
//------------------------------------------------

//----------Using for mailtext--------------------
#define S_MEM_NAME "openmem3.c"
#define S_MEM_ID 1001

#define S_SEM_NAME "opensem3.c"
#define S_SEM_ID 101
//------------------------------------------------
#define OPEN 1
#define GET 0

#define M_VALID 1 //标记一个内存块是否有效
#define M_UNVALID 0
#define TURN_OVER -1

#define SEM_NUM 1
#define SEM_VAL 1


void * mem_start;

union semun{
	int			val;
	struct semid_ds		*buf;
	unsigned short		*array;
};


//用来控制共享内存中内存块
typedef struct mem_head
{
	unsigned  int state;
	unsigned  int len;
}Mhead;

//获得一个共享内存
void * getMem(char * pathname, int id, size_t size, int flag)
{
	key_t mem_key;
	int   mem_id;
	void * mem_ptr;
	Mhead mh={0,0};

	mem_key = ftok(pathname, id);
	if (mem_key == -1)
	{
		perror("ftok error!\n");
		exit(1);
	}

	//有的时候，shmget会报permission dennied错误。
	//可能是ftok时取的pathname权限不够。
	if(flag==OPEN)
	{
		mem_id = shmget(mem_key, size, IPC_CREAT|0666);
		mem_ptr=shmat(mem_id,(void *)0,0);
		mem_start=mem_ptr;
		//保证开辟的共享内存是新鲜的0
		memset(mem_ptr,0,size);
		mh.state=M_UNVALID;
		mh.len=TOTAL_LEN-sizeof(mh);
		memcpy(mem_ptr,&mh,sizeof(mh));

	}
	else if(flag==GET)
	{
		mem_id = shmget(mem_key, 0, IPC_CREAT|0666);//这里有个问题：shmflg取0时，那存取权限怎么解决？？？
		mem_ptr=shmat(mem_id,(void *)0,0);
		mem_start=mem_ptr;
	}
	else
	{
		printf("flag error\n");
		exit(1);
	}


	if (mem_id == -1)
	{
		perror ("shmget call failed.\n");
		exit(1);
	}


	if (mem_ptr ==(void *)(-1))
	{
		perror ("attach shared memory error!\n");
		exit (1);
	}

    return mem_start;
}

//卸掉一块共享内存的连接
void clearMem(void * mem_ptr)
{
	int ret;
	if((ret=shmdt(mem_ptr))==-1)
	{
		perror("shmdt");
		exit(1);
	}
}

//获得一个信号量集合
int getSem(char * pathname,int proj_id,int flag)
{
	key_t key;
	int semid;
	union semun sem_handler;//定义一个信号量集

	key=ftok(pathname,proj_id);
	if(key==-1)
	{
		perror("ftok");
	}

	if(flag==OPEN)
		semid=semget(key,SEM_NUM,IPC_CREAT|0666);
	else if(flag==GET)
		semid=semget(key,0,IPC_CREAT|0666);

	if(semid==-1)
	{
		perror("semget");
	}
	sem_handler.val=SEM_VAL;

	//semid对应的信号量集，对其中的0号信号量，进行SETVAL操作。操作的内容写在sem_handler中
	//这里为了简化，默认的信号量集中元素个数为1
	semctl(semid,0,SETVAL,sem_handler);

	return semid;
}

#define READ_SUCCESS 0
#define READ_FAILED -1


//从dst读数据到sre
int my_read(void ** p_dst,void *sre,int semid)
{
	void *dst=*p_dst;
	//int offset=0;//告诉读指针，应该偏移多少
	Mhead mh={0,0};
	int ret;

	struct sembuf do_P={0,-1,IPC_NOWAIT};
	struct sembuf do_V={0,+1,IPC_NOWAIT};
	int door=0;



	while((door=semctl(semid,0,GETVAL,0))==0)
	{
		usleep(50);
	}

	//做一次P操作
	semop(semid,&do_P,1);

	//从dst所指的共享内存中读取控制头，存到mh地址中
	memcpy(&mh,dst,sizeof(mh));
//printf("current read position %d,current seg's state is %d, len is %d\n",(int)(dst-mem_start),mh.state,mh.len);

	//判断，如果控制字段有效位
	if(mh.state==M_VALID)
	{
		memcpy(sre,dst+sizeof(mh),mh.len);
		mh.state=M_UNVALID;
		//把更新好的控制字段，写回共享内存中
		memcpy(dst,&mh,sizeof(mh));

		//做一次V操作
		semop(semid,&do_V,1);

		*p_dst+=sizeof(mh)+mh.len;
		return mh.len;
	}
	else if(mh.state==M_UNVALID)
	{
		semop(semid,&do_V,1);
		return READ_FAILED;
	}
	else if(mh.state==TURN_OVER)
	{
		semop(semid,&do_V,1);
		*p_dst=mem_start;
		return READ_FAILED;
	}
	else
	{
		printf("error");
		exit(1);
	}

/*	else
	{
		if((ret=check_bottom(dst))==-1)//如果当前内存段为空闲，且为探底段
		{
			memcpy(&mh,mem_start,sizeof(mh));
			if(mh.state==M_VALID)//再去检查头部的内存段是否空闲，若非空闲，说明执行了探底处理
			{
				semop(semid,&do_V,1);
				*p_dst=mem_start;
//printf("goback action~~~\n");
				return READ_FAILED;
			}
			else//如果此空闲内存段为探底段，且头部段也空闲，说明当前无数据可读
			{
				semop(semid,&do_V,1);
				return READ_FAILED;
			}
		}
		else//如果当前内存段为空闲，且非探底段。那么说明当前无数据可读
		{
			semop(semid,&do_V,1);
			return READ_FAILED;
		}
	}*/

}

//检查ptr所指的内存段，空间是否满足。
//返回-1：当前空间不够；返回 0：发生探底；返回 1：空间满足
int check_space(void *dst,int ask_len)
{
	int op_check_bottom;
	Mhead mh={0,0};
	memcpy(&mh,dst,sizeof(Mhead));//获得当前内存段的控制头

	if(mh.state == M_VALID)//如果当前内存段state字段为VALID,那么说明目前没有空闲区
	{
		return -1;
	}

	if(mh.len<ask_len+sizeof(mh))//如果当前内存段state为UN_VALID,那么继续检测空间是否足够
	{
		op_check_bottom=check_bottom(dst);//检测当前内存段是否探底

		if(op_check_bottom>0)//未探底则执行合并
		{
			if(mem_enlarge(dst)==-1)//若返回-1，说明下一个段非空闲，无法合并
			{
				return -1;
			}

 			return check_space(dst,ask_len);//合并后，再查一次
		}
		else //探底
		{
			return 0;
		}
	}
	else
	{
		return 1;//当前内存段空间足够，返回1
	}
}

//内存合并
int mem_enlarge(void *dst)
{
	Mhead mh;
	Mhead next_mh;
	void * next_seg;
	//获得当前 内存段的控制头
	memcpy(&mh,dst,sizeof(Mhead));
	//获取下一个 内存段的控制头
	next_seg = dst+sizeof(Mhead)+mh.len;
	memcpy(&next_mh,next_seg,sizeof(Mhead));
	//检查下一个内存段是否空闲
	if(next_mh.state==M_VALID)
	{
		return -1;//下一个段，state位有效，无法合并
	}
	else//若空闲，则合并之
	{
		mh.len+=next_mh.len+sizeof(Mhead);//合并的时候，要把被合并段的len和首部长度，都加进来
		memcpy(dst,&mh,sizeof(Mhead));
	}
	return 1;
}

//当发现当前内存段不够用的时候，使用本函数，来检测是否探底
int check_bottom(void * ptr)
{
	int offset=0;//offset 是ptr距离mem_start的偏移量
	Mhead mh={0,0};
	memcpy(&mh,ptr,sizeof(mh));

	offset=(int)ptr-(int)mem_start;

	if(TOTAL_LEN==(sizeof(mh)+mh.len+offset))
	{
		return -1;//已经探底
	}
	else
	{
		return 1;//没有探底
	}
}



#define WRITE_FAILED -1
#define WRITE_SUCCESS 0

int my_write(void **p_dst,void *sre,int ask_len,int semid)
{
	void *dst=*p_dst;
	int mw1_ret;
	int door=0;
	void *turn_dst;

	struct sembuf do_P={0,-1,IPC_NOWAIT};
	struct sembuf do_V={0,+1,IPC_NOWAIT};
	while((door=semctl(semid,0,GETVAL,0))==0)
	{
		usleep(50);
	}

	semop(semid,&do_P,1);

//printf("current read position %d,ask_len is %d\n",(int)(dst-mem_start),ask_len);
	mw1_ret=basic_write(dst,sre,ask_len);

	if(mw1_ret==0)//basic_write函数返回 0,说明探底
	{
		turn_dst=mem_start;
//printf("goback action~~~~~~~~~~~~~~~~~~~~~~~~\n");
		mw1_ret=basic_write(turn_dst,sre,ask_len);//经过探底处理后，再做一次写尝试

		if(mw1_ret<0)//如果经过探底处理后，仍返回-1，说明暂时不可写
		{
			semop(semid,&do_V,1);
			//usleep(50);
			*p_dst=mem_start;
			return WRITE_FAILED;
		}
		else//经过探底处理后，再执行basic_write，是不可能返回0值的（因为探底不存在了）
		{
			semop(semid,&do_V,1);
			*p_dst=mem_start;
			return WRITE_SUCCESS;//执行成功	！
		}
	}
	if(mw1_ret>0)
	{
		semop(semid,&do_V,1);
		*p_dst+=mw1_ret;
		return WRITE_SUCCESS;//执行成功！
	}
	if(mw1_ret<0)//basic_write函数返回 负数，说明暂时不可写（当前空闲区不够，且没有可以合并的段）
	{
		semop(semid,&do_V,1);
		usleep(50);
		return WRITE_FAILED;
	}

}





//返回0：内存探底；返回-1：内存不够；返回>0：读写成功
int basic_write(void * dst,void *sre,int ask_len)
{
	int turn_over=0;
	Mhead mh={0,0};
	Mhead buf_mh={0,0};



	int op_check_space=0;//控制写操作
	int op_check_bottom=0;

//-----------------------------------------------------------

	//检查当前内存段是否满足要求
	op_check_space=check_space(dst,ask_len);

	memcpy(&mh,dst,sizeof(Mhead));
	if(op_check_space==0)//探底,需要调头
	{
		mh.state=TURN_OVER;//探底时修改关键字
		memcpy(dst,&mh,sizeof(Mhead));
		return 0;
	}
	if(op_check_space<0)//空间不满足
	{
		return -1;
	}
	if(op_check_space>0)//当前内存段空闲，且空间满足
	{
		//修改控制头
		buf_mh.state=M_VALID;
		buf_mh.len=ask_len;
		//把控制头写回内存段
		memcpy(dst,&buf_mh,sizeof(Mhead));

		//把请求的实际数据，写到内存
		memcpy(dst+sizeof(Mhead),sre,ask_len);

		//把没用完的空闲区辟为新的内存段，这里先构造控制头
		buf_mh.state=M_UNVALID;
		buf_mh.len=mh.len-ask_len-sizeof(Mhead);
		//把下一个内存段的控制头，写到对应位置
		memcpy(dst+sizeof(Mhead)+ask_len,&buf_mh,sizeof(Mhead));

printf("current seg's len is %d, next seg's len is %d\n",ask_len,buf_mh.len);
		return ask_len+sizeof(Mhead);
	}



}

#define T_BACK -1
#define T_OK 0
#define T_BUSY 1
int fuckwrite(void **p_dst,void *sre,int ask_len,int semid)
{
	void *dst=*p_dst;
	Mhead mh={0,0};
	Mhead buf_mh={0,0};
	int door;
	struct sembuf do_P={0,-1,IPC_NOWAIT};
	struct sembuf do_V={0,+1,IPC_NOWAIT};
	while((door=semctl(semid,0,GETVAL,0))==0)
	{
		usleep(50);
	}
//printf("current read position %d,ask_len is %d\n",(int)(dst-mem_start),ask_len);
	semop(semid,&do_P,1);


	int op_check_space=0;//控制写操作
	int op_check_bottom=0;

	//检查当前内存段是否满足要求
	op_check_space=check_space(dst,ask_len);

	memcpy(&mh,dst,sizeof(Mhead));
	if(op_check_space>0)//当前内存段空闲，且空间满足
	{
		//修改控制头
		buf_mh.state=M_VALID;
		buf_mh.len=ask_len;
		//把控制头写回内存段
		memcpy(dst,&buf_mh,sizeof(Mhead));

		//把请求的实际数据，写到内存
		memcpy(dst+sizeof(Mhead),sre,ask_len);

		//把没用完的空闲区辟为新的内存段，这里先构造控制头
		buf_mh.state=M_UNVALID;
		buf_mh.len=mh.len-ask_len-sizeof(Mhead);
		//把下一个内存段的控制头，写到对应位置
		memcpy(dst+sizeof(Mhead)+ask_len,&buf_mh,sizeof(Mhead));

		*p_dst+=sizeof(Mhead)+ask_len;

		//printf("current seg's len is %d, next seg's len is %d\n",ask_len,buf_mh.len);
		semop(semid,&do_V,1);
		return T_OK;
	}
	else if(op_check_space==0)//探底,需要调头
	{
		mh.state=TURN_OVER;//探底时修改关键字
		memcpy(dst,&mh,sizeof(Mhead));

		*p_dst=mem_start;
		semop(semid,&do_V,1);
		return T_BACK;
	}
	else if(op_check_space<0)//空间不满足
	{
		semop(semid,&do_V,1);
		return T_BUSY;
	}

}

#endif






